package com.tianxiabuyi.weixin.common;

import com.tianxiabuyi.utils.HttpUtil;
import com.tianxiabuyi.utils.StringUtil;
import com.tianxiabuyi.weixin.common.exception.WxRuntimeException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 微信客户端的包装
 */
public class WxClient {


    private String clientId;
    private String clientSecret;
    private AccessTokenHolder accessTokenHolder;
    private HttpClient httpClient;

    public WxClient() {
        this.httpClient = new DefaultHttpClient();
    }

    public WxClient(String tokenUrl, String clientId, String clientSecret) {
        this(clientId, clientSecret, new DefaultAccessTokenHolder(tokenUrl, clientId, clientSecret));
    }

    public WxClient(String clientId, String clientSecret, AccessTokenHolder accessTokenHolder) {
        this.clientId = clientId;
        this.clientSecret = clientSecret;
        this.accessTokenHolder = accessTokenHolder;
        this.httpClient = new DefaultHttpClient();
    }

    public String getClientId() {
        return clientId;
    }

    public void setClientId(String clientId) {
        this.clientId = clientId;
    }

    public String getClientSecret() {
        return clientSecret;
    }

    public void setClientSecret(String clientSecret) {
        this.clientSecret = clientSecret;
    }

    private String appendAccessToken(String url) {
        String token = accessTokenHolder.getAccessToken().getAccessToken();
        return url + (url.indexOf('?') == -1 ? "?access_token=" + token : "&access_token=" + token);
    }

    public void refreshToken() {
        accessTokenHolder.refreshToken();
    }

    public AccessToken getAccessToken() {
        return accessTokenHolder.getAccessToken();
    }

    private boolean invalidToken(int code) {
        boolean result = code == 42001 || code == 40001 || code == 40014;
        if (result) {
            accessTokenHolder.expireToken();//强制设置为无效
        }
        return result;
    }

    public String get(String url) {
        return get(url, true);
    }

    public String get(String url, boolean needToken) {
        if (needToken) {
            return HttpUtil.get(appendAccessToken(url));
        } else {
            return HttpUtil.get(url);
        }
    }

    public byte[] getBinary(String url, boolean needToken) {
        if (needToken) {
            try {
                return httpGetBinary(appendAccessToken(url));
            } catch (Exception e) {
                if (e instanceof WxRuntimeException) {
                    WxRuntimeException wxRuntimeException = (WxRuntimeException) e;
                    if (invalidToken(wxRuntimeException.getCode())) {
                        refreshToken();
                        return httpGetBinary(appendAccessToken(url));
                    }
                }
                throw e;
            }
        } else {
            return httpGetBinary(url);
        }
    }


    private byte[] httpGetBinary(String url) {
        HttpGet httpGet = new HttpGet(url);
        HttpResponse response = null;
        String responseContent = null;
        try {
            response = httpClient.execute(httpGet);
            StatusLine statusLine = response.getStatusLine();
            HttpEntity entity = response.getEntity();
            if (statusLine.getStatusCode() >= 300) {
                try {
                    EntityUtils.consume(entity);
                } catch (Exception e1) {

                }
                throw new WxRuntimeException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
            }
            InputStream inputStream = null;
            try {
                inputStream = response.getEntity().getContent();
            } catch (Exception e) {

            }
            return IOUtils.toByteArray(inputStream);

        } catch (IOException ex) {
            throw new WxRuntimeException(999, ex.getMessage());
        }
    }


    private String httpGet(String url) {
        HttpGet httpGet = new HttpGet(url);
        HttpResponse response = null;
        String responseContent = null;
        try {
            response = httpClient.execute(httpGet);
        } catch (Exception e) {

        }
        StatusLine statusLine = response.getStatusLine();
        HttpEntity entity = response.getEntity();
        if (statusLine.getStatusCode() >= 300) {
            try {
                EntityUtils.consume(entity);
            } catch (Exception e1) {

            }
            throw new WxRuntimeException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
        }
        try {
            responseContent = entity == null ? null : EntityUtils.toString(entity, StringUtil.UTF8);
        } catch (Exception e1) {

        }
        return responseContent;
    }

    public File download(String url) {
        try {
            return httpDownload(appendAccessToken(url));
        } catch (Exception e) {
            if (e instanceof WxRuntimeException) {
                WxRuntimeException wxRuntimeException = (WxRuntimeException) e;
                if (invalidToken(wxRuntimeException.getCode())) {
                    refreshToken();
                    return httpDownload(appendAccessToken(url));
                }
            }
            throw e;
        }
    }

    public InputStream copyStream(String url, String post) {
        try {
            return httpCopyFromStream(appendAccessToken(url), post);
        } catch (Exception e) {
            if (e instanceof WxRuntimeException) {
                WxRuntimeException wxRuntimeException = (WxRuntimeException) e;
                if (invalidToken(wxRuntimeException.getCode())) {
                    refreshToken();
                    return httpCopyFromStream(appendAccessToken(url), post);
                }
            }
            throw e;
        }
    }

    /**
     * 永久素材下载使用,奇葩的下载方式
     *
     * @param url
     * @param post
     * @return
     */
    private InputStream httpCopyFromStream(String url, String post) {
        HttpPost httpPost = new HttpPost(url);
        StringEntity stringEntity = null;
        InputStream inputStream = null;
        byte[] binaryContent = null;
        if (post != null) {
            try {
                stringEntity = new StringEntity(post, StringUtil.UTF8);
            } catch (Exception e) {

            }
            httpPost.setEntity(stringEntity);
        }
        HttpResponse response = null;
        try {
            response = httpClient.execute(httpPost);
        } catch (Exception e) {

        }
        StatusLine statusLine = response.getStatusLine();
        HttpEntity entity = response.getEntity();
        if (statusLine.getStatusCode() >= 300) {
            try {
                EntityUtils.consume(entity);
            } catch (Exception e1) {

            }
            throw new WxRuntimeException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
        } else {
            try {
                inputStream = entity.getContent();
                binaryContent = IOUtils.toByteArray(inputStream);
            } catch (Exception e1) {

            }
        }
        return new ByteArrayInputStream(binaryContent);
    }

    private File httpDownload(String url) {
        HttpGet httpGet = new HttpGet(url);
        HttpResponse response = null;
        File tempFile = null;
        try {
            response = httpClient.execute(httpGet);
        } catch (Exception e) {

        }
        StatusLine statusLine = response.getStatusLine();
        HttpEntity entity = response.getEntity();
        if (statusLine.getStatusCode() >= 300) {
            try {
                EntityUtils.consume(entity);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            throw new WxRuntimeException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
        } else {
            Header[] dispositionHeaders = response.getHeaders("Content-disposition");
            if (dispositionHeaders != null && dispositionHeaders.length > 0) {
                String fileName = extractFileName(dispositionHeaders[0].getValue());
                if (fileName == null || "".equals(fileName.trim())) {
                    fileName = UUID.randomUUID().toString();
                }
                InputStream inputStream = null;
                try {
                    inputStream = entity.getContent();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                tempFile = new File(FileUtils.getTempDirectory(), fileName);
                try {
                    FileUtils.copyInputStreamToFile(inputStream, tempFile);
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            } else {
                String errors = null;
                try {
                    errors = entity == null ? null : EntityUtils.toString(entity, StringUtil.UTF8);
                } catch (IOException e1) {
                    e1.printStackTrace();
                }

            }
        }
        return tempFile;
    }

    private String extractFileName(String headerValue) {
        String fileName = null;
        Pattern regex = Pattern.compile("(?<=filename=\").*?(?=\")");
        Matcher regexMatcher = regex.matcher(headerValue);
        if (regexMatcher.find()) {
            fileName = regexMatcher.group();
        }

        return fileName;
    }


    public String post(String url, String content) {
        try {
            return httpPost(appendAccessToken(url), content);
        } catch (Exception e) {
            if (e instanceof WxRuntimeException) {
                WxRuntimeException wxRuntimeException = (WxRuntimeException) e;
                if (invalidToken(wxRuntimeException.getCode())) {
                    refreshToken();
                    return httpPost(appendAccessToken(url), content);
                }
            }
            throw e;
        }
    }

    // 上传文件
    public  String upload(String filepath, String url) {
        File file = new File(filepath);
        HttpClient client = new DefaultHttpClient();
        url = appendAccessToken(url );
        HttpPost post = new HttpPost(url);
        FileBody fileBody = new FileBody(file);
        MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
        multipartEntityBuilder.addBinaryBody("media", file).setMode(HttpMultipartMode.RFC6532);
        post.setEntity(multipartEntityBuilder.build());
        HttpResponse response = null;
        String json = null;
        try {
            post.setEntity(multipartEntityBuilder.build());
            response = client.execute(post);
            json = EntityUtils.toString(response.getEntity());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return json ;
    }

    private String httpPost(String url, String content) {
        HttpPost httpPost = new HttpPost(url);
        HttpResponse response = null;
        String responseContent = null;
        if (content != null) {
            StringEntity entity = null;
            try {
                entity = new StringEntity(content, StringUtil.UTF8);
            } catch (Exception e) {
                e.printStackTrace();
            }
            httpPost.setEntity(entity);
        }
        try {
            response = httpClient.execute(httpPost);
        } catch (Exception e) {

        }
        StatusLine statusLine = response.getStatusLine();
        HttpEntity entity = response.getEntity();
        if (statusLine.getStatusCode() >= 300) {
            try {
                EntityUtils.consume(entity);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            throw new WxRuntimeException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
        }
        try {
            responseContent = entity == null ? null : EntityUtils.toString(entity, StringUtil.UTF8);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        return responseContent;
    }

    private String httpPost(String url, File file) {
        return httpPost(url, file, null);
    }

    private String httpPost(String url, File file, Map<String, String> form) {
        HttpPost httpPost = new HttpPost(url);
        HttpResponse response = null;
        HttpEntity entity = null;
        if (file != null) {
            MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
            multipartEntityBuilder.addBinaryBody("media", file).setMode(HttpMultipartMode.RFC6532);
            if (form != null && !form.isEmpty()) {
                for (String key : form.keySet()) {
                    multipartEntityBuilder.addTextBody(key, form.get(key));
                }
            }
            httpPost.setEntity(multipartEntityBuilder.build());
            httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString());
        }

        try {
            response = httpClient.execute(httpPost);
        } catch (Exception e) {

        }
        StatusLine statusLine = response.getStatusLine();
        entity = response.getEntity();
        if (statusLine.getStatusCode() >= 300) {
            try {
                EntityUtils.consume(entity);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            throw new WxRuntimeException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
        }
        String result = "";
        try {
            result = entity == null ? null : EntityUtils.toString(entity, StringUtil.UTF8);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }
}
